%% Function use to transform to the different regions and modes used by different authors
% and sources
% ModesOrRegions: transform to modes or regions (insert string)
% Transformto: Transform to what set?

function [cfg,exvars,depvars,indepvars,varargout] = TransformModesandRegions(cfg,exvars,depvars,indepvars,Transformto, varargin)
cfg.Transformto  = Transformto;
DisplayMessage   = false;

%% First things first
if (sum(strcmp({exvars.ModeSet, cfg.ModeSet},{Transformto,Transformto}))==0 && sum(strcmp({exvars.RegionSet, cfg.RegionSet},{Transformto,Transformto}))==0) && ~isempty(depvars)
       vartypes    = {'exvars','depvars','indepvars'};
elseif  sum(strcmp({exvars.ModeSet},{Transformto}))==0 && sum(strcmp({exvars.RegionSet},{Transformto}))==0
        vartypes   = {'exvars'};
        disp(['Only transforming exvars to ' cfg.Transformto]);
elseif  sum(strcmp({cfg.ModeSet},{Transformto}))==0 && sum(strcmp({cfg.RegionSet},{Transformto}))==0
        vartypes   = {'depvars','indepvars'};
        disp(['Only transforming indepvars & depvars to ' cfg.Transformto]);
end

blacklist   = {'depvars.PreferenceFactor','depvars.errors'};
if isempty(depvars) || sum(depvars.ModeSplit(1,5,:))==0
    ModeSplit               = exvars.TravelDemand ./ repmat(sum(exvars.TravelDemand,3),[1 1 size(exvars.TravelDemand,3)]);
    ModeSplit(:,36:130,:)   = zeros([size(exvars.TravelDemand,1) 95 size(exvars.TravelDemand,3)]);
    TravelDemand            = exvars.TravelDemand .* repmat(exvars.Population(:,1:35), [1 1 size(exvars.TravelDemand,3)]);
    TravelDemand(:,36:130,:)= zeros([size(exvars.TravelDemand,1) 95 size(exvars.TravelDemand,3)]);
else
    ModeSplit               = depvars.ModeSplit;
    Population              = exvars.Population;
    TravelDemandTotal       = depvars.TravelDemand .* repmat(exvars.Population, [1 1 size(exvars.TravelDemand,3)]);
end
       
if (strcmp(cfg.Transformto,cfg.ModeSet ) || strcmp(cfg.Transformto,cfg.RegionSet))  % Makes sure we're not changing the set into the original set
    if DisplayMessage; disp (['Already in ' cfg.Transformto]);end
    switch Transformto(1)
        case 'r'
        varargout{1} = 1:length(cfg.allregions);
        case 'm'
        varargout{1} = 1:length(cfg.modes);
    end
else
        disp (['Converting to ' Transformto]);
switch cfg.Transformto
    case {'modes3','modes4','modes7'}
        %% Transform between different sets of modes
            cfg.modetransform.modes3(:,:)   = ([0 0 1 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 0 1]');
            cfg.modetransform.modes4(:,:)   = ([1 1 0 0 0 0 0; 0 0 1 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 1 1]');
            cfg.modetransform.modes7(:,:)   = (eye(7));
            cfg.modetransform.transformation = (cfg.modetransform.(cfg.ModeSet)' * cfg.modetransform.(cfg.Transformto));
        
        switch cfg.Transformto
            case 'modes7' % As used in TIMER-TRAVEL
                cfg.modes               ...
                = {'walk','bike','bus','train','car','hs-train','air'}; % Car includes motorcycle
                varargout{1} = 1:7;
                cfg.ModesFasterThanLDV = 5:7;
            case 'modes4' % As used in GCAM and POLES
                cfg.modes               ...
                    = {'NM','PT','LDV','HSM'}; % Public transportation (Bus + Train), Light duty vehicle (Car + Motorcycle), High speed (air+HS train)
                varargout{1} = 1:4;
                cfg.ModesFasterThanLDV = 3:4;
            case 'modes3'
                 cfg.modes               ...
                    = {'PubTrans','Car','Air'}; % Public transportation (Bus + Train), Light duty vehicle (Car + Motorcycle), High speed (air+HS train)
                varargout{1} = 1:4;
                cfg.ModesFasterThanLDV = 2:3;
        end
       
       if eval(['~isempty(' vartypes{1} ')'])
       UseModes = exvars.TravelDemand(:,35,:)~=0;
       for n = 1:length(vartypes)
           eval(['config.fields = fieldnames(' vartypes{n} ');']);
           for m = 1:length(config.fields)
               eval(['[matrixsize(1) matrixsize(2) matrixsize(3)] = size(' vartypes{n} '.' config.fields{m} ');']);
               
                if matrixsize(3) > 1 && ~sum(strcmp([vartypes{n} '.' config.fields{m}],blacklist ))
                    eval(['temp.' vartypes{n} '.' config.fields{m} ' = zeros([matrixsize(1:2), length(cfg.modes)]);']);
                    eval([vartypes{n} '.' config.fields{m} '=' ' double('  vartypes{n} '.' config.fields{m} ');'])
                    for r = 1:length(cfg.allregions)
                        if strcmp(cfg.ModeSet,'modes7'); modes1 = 'UseModes(r,1,:)'; else modes1=':'; end
                        if strcmp(cfg.Transformto,'modes7'); modes2 = 'UseModes(r,1,:)'; else modes2=':'; end
                        
                        for t = 1:matrixsize(2)
                            switch config.fields{m}
                                case 'Speed'
                                eval(['temp.' vartypes{n} '.' config.fields{m} '(r,t,:) = '...
                                ' (permute((' vartypes{n} '.' config.fields{m} '(r,t,' modes1 ').^-1) .* ModeSplit(r,t,' modes1 '),[2,3,1]) * cfg.modetransform.transformation(' modes1 ',' modes2 ' )' ...
                                './ (permute(ModeSplit(r,t,' modes1 '),[2,3,1]) * cfg.modetransform.transformation(' modes1 ',:))).^-1;' ]);

                                case 'TravelDemand'
                                eval(['temp.' vartypes{n} '.' config.fields{m} '(r,t,:) = '...
                                ' permute(' vartypes{n} '.' config.fields{m} '(r,t,' modes1 '),[2,3,1])  * cfg.modetransform.transformation(' modes1 ',' modes2 ' );']);
                            
                                otherwise

                                eval(['temp.' vartypes{n} '.' config.fields{m} '(r,t,:) = '...
                                ' permute(' vartypes{n} '.' config.fields{m} '(r,t,' modes1 ') .* ModeSplit(r,t,' modes1 '),[2,3,1]) * cfg.modetransform.transformation(' modes1 ',' modes2 ' )' ...
                                './ (permute(ModeSplit(r,t,' modes1 '),[2,3,1]) * cfg.modetransform.transformation(' modes1 ',:));' ]);
          
                            end
                        end
                    end
                    eval([vartypes{n} '.' config.fields{m} ' =' 'temp.' vartypes{n} '.' config.fields{m} ';']);
                end
           end
       end
       end
       
cfg.ModeSet = cfg.Transformto;

    case {'regions3','regions11','regions26'}
        %% Transform between difference sets of regions
        cfg.regionconversion.regions3  = [1,1,3,3,3,3,3,3,3,3,1,2,1,2,2,2,3,3,3,3,3,3,1,1,3,3,4];
        cfg.regionconversion.regions11 = [1,1,8,8,8,8,9,6,6,6,3,4,3,5,5,5,9,11,10,7,10,10,2,2,11,6,12];
        cfg.regionconversion.regions26 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27];
        
        cfg.regiontransform.(cfg.Transformto)     = zeros(27,max(cfg.regionconversion.(cfg.Transformto)));
        cfg.regiontransform.(cfg.Transformto)(sub2ind([27 max(cfg.regionconversion.(cfg.Transformto))],1:27,cfg.regionconversion.(cfg.Transformto))) = 1;
        
        cfg.regiontransform.(cfg.RegionSet)   = zeros(27,max(cfg.regionconversion.(cfg.RegionSet)));
        cfg.regiontransform.(cfg.RegionSet)(sub2ind([27 max(cfg.regionconversion.(cfg.RegionSet))],1:27,cfg.regionconversion.(cfg.RegionSet))) = 1;
        
        cfg.regiontransform.transformation    = logical(cfg.regiontransform.(cfg.RegionSet)' * cfg.regiontransform.(cfg.Transformto));
        
        switch cfg.Transformto
            case 'regions3' % Used by GCAM
                cfg.allregions = ...
                    {'Industrialized','Reforming','Developing','World'};
                cfg.shortregions = {'Ind','Ref','Dev','Wor'};
                varargout{1} = 1:4;
            case 'regions11' % Used by Schafer & policy paper
                cfg.allregions = ...
                    {'North America','Pacific OECD','Western Europe','Eastern Europe',...
                    'Former Soviet Union','Sub-Saharan Africa','Centrally Planned Asia',...
                    'Latin America','Middle East & North Africa','Other Pacific Asia',...
                    'South Asia','World'};
                cfg.shortregions = {'NAM','PAO','WEU','EEU','FSU','Afr','CPA','LAM','MEA','PAS','SA','Wor'};
                varargout{1} = 1:12;
            case 'regions26' % Used by TIMER
                cfg.allregions = {'Canada','USA','Mexico','Rest Central America','Brazil','Rest South America' ...
                    ,'Northern Africa','Western Africa','Eastern Africa','South Africa','OECD Europe' ...
                    ,'Eastern Europe','Turkey','Ukraine +','Asia-Stan','Russia +','Middle East' ...
                    ,'India','Korea','China +','South East Asia','Indonesia +','Japan','Oceania' ...
                    ,'Rest of S. Asia','Rest of S. Africa', 'World'};
                 cfg.shortregions = {'Canada','USA','Mexico','Rest Central America','Brazil','Rest South America' ...
                    ,'Northern Africa','Western Africa','Eastern Africa','South Africa','OECD Europe' ...
                    ,'Eastern Europe','Turkey','Ukraine +','Asia-Stan','Russia +','Middle East' ...
                    ,'India','Korea','China +','South East Asia','Indonesia +','Japan','Oceania' ...
                    ,'Rest of S. Asia','Rest of S. Africa', 'World'};
                varargout{1}=1:27;
        end
       
       if eval(['~isempty(' vartypes{1} ')'])
       for n = 1:length(vartypes)
           eval(['config.fields = fieldnames(' vartypes{n} ');']);
           for m = 1:length(config.fields)
               eval(['[matrixsize(1) matrixsize(2) matrixsize(3)] = size(' vartypes{n} '.' config.fields{m} ');']);
               
                if matrixsize(1) > 1 && ~sum(strcmp([vartypes{n} '.' config.fields{m}],blacklist ))>0
                    eval(['temp.' vartypes{n} '.' config.fields{m} ' = zeros([length(cfg.allregions) matrixsize(2:3) ]);']);
                    for mode = 1:matrixsize(3)
                        for t = 1:matrixsize(2)
                            switch config.fields{m}
                            
                                case 'Speed'
                                eval(['temp.' vartypes{n} '.' config.fields{m} '(:,t,mode) = '...
                                ' (permute((' vartypes{n} '.' config.fields{m} '(:,t,mode).^-1) .* TravelDemandTotal(:,t,mode),[2,1,3]) * cfg.regiontransform.transformation' ...
                                './ (permute(TravelDemandTotal(:,t,mode),[2,1,3]) * cfg.regiontransform.transformation)).^-1;' ]);    

                                case 'Population'
                                     eval(['temp.' vartypes{n} '.' config.fields{m} '(:,t,mode) = '...
                                ' permute(' vartypes{n} '.' config.fields{m} '(:,t,mode),[2,1,3]) * cfg.regiontransform.transformation;']);  
                            
                                case {'TravelPrice','FasolinePrice','FuelPrice','LoadFactor','ModeSplit','TMB'}
                                    eval(['temp.' vartypes{n} '.' config.fields{m} '(:,t,mode) = '...
                                ' permute(' vartypes{n} '.' config.fields{m} '(:,t,mode) .* TravelDemandTotal(:,t,mode),[2,1,3]) * cfg.regiontransform.transformation' ...
                                './ (permute(TravelDemandTotal(:,t,mode),[2,1,3]) * cfg.regiontransform.transformation);' ]);       
                            
                                otherwise
                                eval(['temp.' vartypes{n} '.' config.fields{m} '(:,t,mode) = '...
                                '((permute(' vartypes{n} '.' config.fields{m} '(:,t,mode),[2,1,3]) .* transpose(Population(:,t,1)))'...
                                '* (cfg.regiontransform.transformation))./(transpose(Population(:,t,1))*cfg.regiontransform.transformation);' ]);  
                            end
                        end
                    end
                    eval([vartypes{n} '.' config.fields{m} ' =' 'temp.' vartypes{n} '.' config.fields{m} ';']);
                end
           end
       end
       end
       cfg.RegionSet = cfg.Transformto;
end

indepvars.UseModes  = exvars.TravelDemand(:,35,:)~=0;

if cfg.NoUnMotorized
    if length(cfg.modes)==4
        indepvars.UseModes(:,:,strcmp(cfg.modes,'NM')) = false;
    else
       indepvars.UseModes(:,:,strcmp(cfg.modes,'Walk')|strcmp(cfg.modes,'Bike')) = false;
    end
end
end
end